package org.marlin.http.http;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpHead;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.InputStreamEntity;
import org.apache.http.entity.StringEntity;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpMethod;


public final class HttpUtil {
    private static final Logger log = LoggerFactory.getLogger(HttpUtil.class);

    private static Logger logger = LoggerFactory.getLogger(HttpUtil.class);
    public static final String AUTHORIZATION = "Authorization";

    public static HttpUriRequest makeRequest(Uri uri, String url, Object body) {
        if (url.startsWith("/")) {
            url = uri.getHostSpace() + url;
        } else {
            url = uri.getHostSpace() + "/" + url;
        }


        HttpRequestBase httpRequestBase = null;
        switch (HttpMethod.valueOf(uri.getMethod())) {
            case GET:
                return (HttpUriRequest) buildGet(url, body);

            case POST:
                return (HttpUriRequest) buildPost(url, body);

            case DELETE:
                return (HttpUriRequest) new HttpDelete(url);

            case HEAD:
                return (HttpUriRequest) new HttpHead(url);

            case PATCH:
                return (HttpUriRequest) buildX5(url, body);
            case PUT:
                return (HttpUriRequest) buildPut(url, body);
        }
        HttpRequestBase httpRequestBase1 = buildPost(url, body);
        return (HttpUriRequest) httpRequestBase1;
    }


    public static final String SOCKET_TIMEOUT = "http.socket.timeout";

    public static final int TIME_OUT = 59000;


    public static <T> T readRespContent(HttpResponse response, Charset defaultCharset, Uri uri, Class<T> returnType) {
        if (null == response || null == response.getEntity()) {
            return null;
        }
        String content = "";
        try {
            content = EntityUtils.toString(response.getEntity(), defaultCharset);
            return (T) castContent(content, uri, (Class) returnType);
        } catch (Exception e) {
            log.info("read response:", e);
        } finally {
            try {
                EntityUtils.consume(response.getEntity());
            } catch (Exception e) {
                logger.error("close response error", e);
            }
        }
        return null;
    }

    public static <T> T castContent(Object content, Uri uri, Class<T> type) {
        HttpReturnType returnType = uri.getType();
        if (returnType == HttpReturnType.TABLE && type.equals(List.class) && content instanceof String) {
            return (T) parse(content.toString());
        }
        return castContent(content, type, uri.getReturnClass());
    }


    private static List<String> parse(String content) {
        List<String> result = new ArrayList<>();
        String[] split = content.split("\n");
        for (String index : split) {
            result.add(index.trim());
        }
        return result;
    }

    public static <T, D> T castContent(Object content, Class<T> type, Class<D> dClass) {
        if (type.equals(String.class)) {
            return type.cast(content);
        }
        if (type.equals(Void.class)) {
            return type.cast(content);
        }
        if (content instanceof JSON) {
            return (T) JSON.toJavaObject((JSON) content, type);
        }
        if (List.class.equals(type)) {
            return (T) JSON.parseArray(content.toString(), dClass);
        }
        return (T) JSON.parseObject(content.toString(), type);
    }

    private static HttpRequestBase buildX5(String url, Object body) {
        HttpPost httpPost = new HttpPost(url);
        return (HttpRequestBase) httpPost;
    }


    private static HttpRequestBase buildPost(String url, Object body) {
        HttpPost httpPost = new HttpPost(url);
        HttpEntity bodyEntity = getHttpEntity(body);
        httpPost.setEntity(bodyEntity);
        return (HttpRequestBase) httpPost;
    }


    private static HttpRequestBase buildPut(String url, Object body) {
        HttpPut httpPut = new HttpPut(url);
        HttpEntity bodyEntity = getHttpEntity(body);
        httpPut.setEntity(bodyEntity);
        return (HttpRequestBase) httpPut;
    }


    private static HttpEntity getHttpEntity(Object body) {
        if (body instanceof Body) {

            byte[] b = new byte[0];
            try {
                b = JSON.toJSONString(body).getBytes("utf-8");
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
            InputStream in = new ByteArrayInputStream(b, 0, b.length);

            return (HttpEntity) new InputStreamEntity(in, b.length, ContentType.APPLICATION_JSON);
        }
        return (HttpEntity) new StringEntity(JSONObject.toJSONString(body), ContentType.APPLICATION_JSON);
    }


    private static HttpGet buildGet(String url, Object body) {
        HttpGet httpGet = new HttpGet(makeGetUrl(url, body));
        return httpGet;
    }


    private static String makeGetUrl(String url, Object body) {
        StringBuilder sb = new StringBuilder();
        if (!url.contains("?")) {
            sb.append(url + "?");
        } else {
            sb.append(url + "&");
        }
        if (body instanceof Body) {

            Body xb = (Body) body;
            for (String key : xb.keySet()) {
                sb.append(key + "=" + String.valueOf(xb.get(key)) + "&");
            }
        }
        return sb.toString();
    }
}

